package org.kazao.sms;

import java.io.*;
import java.util.*;
import java.util.logging.*;
import javax.comm.*;

class KazaoSerialDriver  implements SerialPortEventListener {
    private static final int RECV_TIMEOUT = 20 * 1000;
    
    private static final int BUFFER_SIZE = 1048576;
    
    private static final int DELAY_BETWEEN_CHARS = 0; /* Default 0 */
    
    private String port;
    private int baud;
    private int dataBits;
    private int stopBits;
    private int parity;
    
    private CommPortIdentifier portId;
    private SerialPort serialPort;
    private InputStream inStream;
    private OutputStream outStream;
    
    private Logger log;
    
    public KazaoSerialDriver(String port, int baud, Logger log) {
        this.port = port;
        this.baud = baud;
        this.log = log;
        dataBits = SerialPort.DATABITS_8;
        stopBits = SerialPort.STOPBITS_1;
        parity = SerialPort.PARITY_NONE;
    }
    
    public void setPort(String port) {
        this.port = port;
    }
    public String getPort() {
        return port;
    }
    public int getBaud() {
        return baud;
    }
    public int getDataBits() {
        return dataBits;
    }
    public int getStopBits() {
        return stopBits;
    }
    public int getParity() {
        return parity;
    }
    
    public boolean open() throws Exception 	{
        boolean result = false;
        Enumeration portList;
        
        log.log(Level.INFO, "Connecting...");
        portList = CommPortIdentifier.getPortIdentifiers();
        while (portList.hasMoreElements()) {
            portId = (CommPortIdentifier) portList.nextElement();
            if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
                if (portId.getName().equalsIgnoreCase(getPort())) {
                    serialPort = (SerialPort) portId.open("Kazao SMS 1.0", 1000);
                    inStream = serialPort.getInputStream();
                    outStream = serialPort.getOutputStream();
                    serialPort.notifyOnDataAvailable(true);
                    serialPort.notifyOnOutputEmpty(true);
                    serialPort.notifyOnBreakInterrupt(true);
                    serialPort.notifyOnFramingError(true);
                    serialPort.notifyOnOverrunError(true);
                    serialPort.notifyOnParityError(true);
                    serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
                    serialPort.addEventListener(this);
                    serialPort.setSerialPortParams(getBaud(), getDataBits(), getStopBits(), getParity());
                    serialPort.setInputBufferSize(BUFFER_SIZE);
                    serialPort.setOutputBufferSize(BUFFER_SIZE);
                    serialPort.enableReceiveTimeout(RECV_TIMEOUT);
                    result = true;
                }
            }
        }
        return result;
    }
    
    public void close() {
        log.log(Level.INFO, "Disconnecting...");
        try { 
            serialPort.close(); 
        } catch (Exception e) {
        }
    }
    
    public void serialEvent(SerialPortEvent event) {
        switch(event.getEventType()) {
            case SerialPortEvent.BI:
                break;
            case SerialPortEvent.OE:
                log.log(Level.SEVERE, "COMM-ERROR: Overrun Error!");
                break;
            case SerialPortEvent.FE:
                log.log(Level.SEVERE, "COMM-ERROR: Framing Error!");
                break;
            case SerialPortEvent.PE:
                log.log(Level.SEVERE, "COMM-ERROR: Parity Error!");
                break;
            case SerialPortEvent.CD:
                break;
            case SerialPortEvent.CTS:
                break;
            case SerialPortEvent.DSR:
                break;
            case SerialPortEvent.RI:
                break;
            case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
                break;
            case SerialPortEvent.DATA_AVAILABLE:
                break;
        }
    }
    public void clearBuffer() throws Exception {
        while (dataAvailable()) {
            inStream.read();
        }
    }
    
    public void send(String s) throws Exception {
        log.log(Level.FINER, "TE: " + s);
        for (int i = 0; i < s.length(); i ++) {
            try { 
                Thread.sleep(DELAY_BETWEEN_CHARS); 
            } catch (Exception e) {
            }
            outStream.write((byte) s.charAt(i));
            outStream.flush();
        }
    }
    
    public void send(char c) throws Exception {
        outStream.write((byte) c);
        outStream.flush();
    }
    
    public void skipBytes(int numOfBytes) throws Exception {
        int count, c;        
        count = 0;
        while (count < 4) {
            c = inStream.read();
            if (c != -1) {
                count++;
            }
        }
    }
    
    public boolean dataAvailable() throws Exception {
        return (inStream.available() > 0 ? true : false);
    }
    
    public String getResponse() throws Exception {
        StringBuffer buffer;
        int c;
        
        buffer = new StringBuffer(256);
        
        while (true) {
            c = inStream.read();
            if (c == -1) {
                buffer.delete(0, buffer.length());
                break;
            }
            buffer.append((char) c);
            if ((buffer.toString().indexOf("OK\r") > -1) || ((buffer.toString().indexOf("ERROR") > -1) && (buffer.toString().indexOf("\r") > -1))) {
                break;
            }
        }
        log.log(Level.FINER, "ME: " + buffer);
        while ((buffer.charAt(0) == 13) || (buffer.charAt(0) == 10)) {
            buffer.delete(0, 1);
        }
        return buffer.toString();
    }
}
